#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<ctype.h>
#include<stdlib.h>
#include<cerrno>
#include<iostream>
#include<string>

#define MAXSIZE 128
#define MAXARGS 32

//命令行参数表
char* gargv[MAXARGS];
int gargc=0;
//命令行参数分隔符
const char* gsep=" ";


//环境变量表
char* genv[MAXARGS];
int genc=0;


//自主shell目前所处的工作路径
char cwd[MAXSIZE];

//记录的最近一个命令执行完毕的退出码
int lastcode=0;


//表明重定向信息
#define NoneRedir 0
#define InputRedir 1
#define AppRedir 2
#define OutputRedir 3

int redir_type=NoneRedir;
char* filename=NULL;





void LoadEnv()
{
  extern char** environ;
  for(;environ[genc];genc++)
  {
    genv[genc]=(char*)malloc(sizeof(char*)*4096);
    strcpy(genv[genc],environ[genc]);
  }
  genv[genc]=NULL;

  //test:
  //printf("Load env:\n");
  //for(int i=0;genv[i];i++)
  //{
  //  printf("genv[%d]:%s\n",i,genv[i]);
  //}

}


static std::string rfindDir(const std::string& p)
{
  if(p=="/")
    return p;
  const std::string psep="/";
  auto pos=p.rfind(psep);
  if(pos==std::string::npos)
    return std::string();
  return p.substr(pos+1);
}



const char* GetUserName()
{
  char* name=getenv("USER");
  if(!name)
    return "None";
  return name;
}


const char* GetHostName()
{
  char* hostname=getenv("HOSTNAME");
  if(!hostname)
    return "None";
  return hostname;
}


const char* GetPwd()
{
  char* pwd=getenv("PWD");
  if(!pwd)
    return "None";
  return pwd;
}



void PrintCommandLine()
{
  printf("[%s@%s %s]# ",GetUserName(),GetHostName(),rfindDir(GetPwd()).c_str());//用户名@主机名 当前路径
  fflush(stdout);
}

int GetCommand(char commandline[],int size)
{
  if(NULL==fgets(commandline,size,stdin))
    return 0;
  commandline[strlen(commandline)-1]='\0';
  return strlen(commandline);
}


void ParseCommand(char commandline[])
{
  //清除全局变量命令行参数中的上一次数据
  gargc=0;
  memset(gargv,0,sizeof(gargv));

  //获取最新一次的命令行参数
  gargv[gargc]=strtok(commandline,gsep);
  while((gargv[++gargc]=strtok(NULL,gsep)));

  //test
  //for(int i=0;gargv[i];i++)
  //{
  //  printf("gargv[%d]:%s\n",i,gargv[i]);
  //}

}



//------------------------------------待改进
int CheckBuiltinExcute()
{
  if(strcmp(gargv[0],"cd")==0)
  {
    //内建命令
    if(gargc>=2)
    {
      //新的目标路径：gargv[1]
      //1.更改进程内核中的路径
      if(chdir(gargv[1])==-1)
      {
        lastcode=errno;
        return -1;
      }
      //更改环境变量
      char pwd[1024];
      getcwd(pwd,sizeof(pwd));
      snprintf(cwd,sizeof(cwd),"PWD=%s",pwd);
      putenv(cwd);
      lastcode=0;
    }
    return 1;
  }
  else if(strcmp(gargv[0],"echo")==0)
  {
    if(gargc==2)
    {
      if(gargv[1][0]=='$')
      {
        if(strcmp(gargv[1]+1,"?")==0)
        {
          printf("%d\n",lastcode);
        }
        else if(strcmp(gargv[1]+1,"PATH")==0)
        {
          printf("%s\n",getenv("PATH"));
        }
        lastcode=0;
      }
    }
    return 1;
  }

  return 0;
}


int ExecuteCommand()
{
  pid_t id=fork();
  if(id<0)
    return -1;
  else if(id==0)
  {
    //子进程
    int fd=-1;
    if(redir_type==NoneRedir)
    {
      //Do Nothing
    }
    else if(redir_type==InputRedir)
    {
      fd=open(filename,O_RDONLY);
      dup2(fd,0);
    }
    else if(redir_type==OutputRedir)
    {
      fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
      dup2(fd,1);
    }
    else if(redir_type==AppRedir)
    {
      fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
      dup2(fd,1);
    }
    else
    {
      //bug
      perror("sun pcb redir");
      exit(-1);
    }
    execvpe(gargv[0],gargv,genv);
    exit(1);
  }
  else
  {
    //父进程
    int status=0;
    pid_t rid=waitpid(id,&status,0);

    lastcode=WEXITSTATUS(status);
    if(rid<0)
    {
      printf("Process waiting failed!");
    }
  }

  return 0;
}


#define TrimSpace(start) do{\
  while(isspace(*start)) start++;\
}while(0)


void ParseRedir(char command_line[])
{

  redir_type=NoneRedir;
  filename=NULL;
  char* start=command_line;
  char* end=start+strlen(command_line);
  while(start<end)
  {
    if(*start=='>')
    {
      if(*(start+1)=='>')
      {
        *start=0;
        start++;
        //start=0; //这里错了，这里直接对指针赋值了
        *start = 0;
        start++;
        redir_type=AppRedir;
      }
      else
      {
        *start=0;
        start++;
        redir_type=OutputRedir;
      }
      TrimSpace(start);
      filename=start;
      break;
    }
    else if(*start=='<')
    {
      *start=0;
      start++;
      redir_type=InputRedir;
      TrimSpace(start);
      filename=start;
      break;
    }
    start++;
  }
}

int main()
{
  //从配置文件中获取环境变量填充环境变量表
  LoadEnv();

  char command_line[MAXSIZE]={0};

  while(1)
  {
    //1.打印命令行字符串
    PrintCommandLine();

    //2.获取用户输入
    if(GetCommand(command_line,sizeof(command_line))==0)
      continue;

    //printf("%s\n",command_line);

    //3.重定向判断及定向前操作
    ParseRedir(command_line);
    //printf("command: %s\n",command_line);
    //printf("redir type: %d\n",redir_type);
    //printf("filename: %s\n",filename);

    //4.解析字符串->命令行解释器，对输入命令字符串首先进行解析
    ParseCommand(command_line);

    //5.判断是否为内建命令-> 父进程自己执行 or 交给子进程执行
    int rcheck=CheckBuiltinExcute();
    if(rcheck==1)
    {
      continue;
    }
    else if(rcheck==-1)
    {
      printf("Input error!\n");
    }

    //让子进程执行命令
    ExecuteCommand();

  }


  return 0;
}
